package zookeeper

import (
	"errors"
	"fmt"
	"github.com/avast/retry-go"
	"github.com/go-zookeeper/zk"
	"strings"
	"sync"
	"time"
	"wf-deploy-agent/conf"
	"wf-deploy-agent/infra"
)

func connect() (*zk.Conn, error) {
	conn, _, err := zk.Connect(conf.CF.Registry.Host, 10*time.Second)
	return conn, err
}

func New() *Client {
	conn, err := connect()
	if err != nil {
		infra.Logger.Fatalln(err)
	}
	return &Client{
		connection: conn,
	}
}

func NewCallback(cb zk.EventCallback) *Client {
	conn, _, err := zk.Connect(conf.CF.Registry.Host, 10*time.Second, zk.WithEventCallback(cb))
	if err != nil {
		infra.Logger.Fatalln(err)
	}

	return &Client{
		connection: conn,
	}
}

type Client struct {
	connection *zk.Conn
}

var mutex sync.Mutex

func (z *Client) GetConn() *zk.Conn {
	mutex.Lock()

	_ = retry.Do(
		func() error {
			state := z.connection.State()
			if state == zk.StateDisconnected || state == zk.StateExpired {
				conn, err := connect()
				if err != nil {
					return err
				}
				z.connection = conn
			}
			return nil
		},
		retry.OnRetry(func(n uint, err error) {
			infra.Logger.Errorf("create zookeeper connection occurs error and now retrying, retry count is %d, error:%v", n, err)
		}),
	)

	mutex.Unlock()
	return z.connection
}

// CreateZnode 创建znode
func (z *Client) CreateZnode(path string, data []byte, flags int32) (string, error) {
	if path == "" {
		infra.Logger.Errorln("path is empty")
	}

	newStrs := make([]string, 0)
	strs := strings.Split(path, "/")
	for _, v := range strs {
		if v == "" {
			continue
		}

		newStrs = append(newStrs, v)
	}

	dir := ""
	for k, v := range newStrs {
		if v == "" {
			continue
		}

		dir += "/" + v
		res, _, err := z.GetConn().Exists(dir)
		if err != nil {
			infra.Logger.Errorln(err)
		}

		if res {
			continue
		}

		if k != len(newStrs)-1 {
			newPath, err := z.GetConn().Create(dir, []byte(""), flags, z.GetAcl())
			if err != nil {
				infra.Logger.Errorf("create znode [%s] occurs error,%v", dir, err)
			} else {
				infra.Logger.Debugf("create znode [%s] succeeded,new znode path is [%s]", dir, newPath)
			}
		} else {
			newPath, err := z.GetConn().Create(dir, data, flags, z.GetAcl())
			if err != nil {
				infra.Logger.Errorf("create znode [%s] occurs error,%v", dir, err)
			} else {
				infra.Logger.Debugf("create znode [%s] succeeded,new znode path is [%s]", dir, newPath)
			}
		}
	}

	return dir, nil
}

// DeleteZnode 删除znode
func (z *Client) DeleteZnode(path string) error {
	_, stat, err := z.GetConn().Get(path)
	if err != nil {
		if errors.Is(err, zk.ErrNodeExists) {
			return nil
		}
		return err
	}

	return z.GetConn().Delete(path, stat.Version)
}

// Exists 验证节点是否存在
func (z *Client) Exists(path string) (bool, error) {
	b, _, e := z.GetConn().Exists(path)

	if e != nil {
		return false, e
	}

	return b, e
}

// GetChildren 获取子节点
func (z *Client) GetChildren(path string) ([]string, error) {
	items, stat, err := z.GetConn().Children(path)
	if err != nil {
		return nil, err
	}

	fmt.Sprintln(stat)
	return items, nil
}

// GetValue 获取节点value
func (z *Client) GetValue(path string) ([]byte, error) {
	bytes, stat, err := z.GetConn().Get(path)
	if err != nil {
		return nil, err
	}

	fmt.Sprintln(stat)
	return bytes, nil
}

func (z *Client) GetAcl() []zk.ACL {
	return zk.WorldACL(zk.PermAll)
}
